package cn.donting.jme.expansion.inspect;

import cn.donting.jme.EditorContent;
import cn.donting.jme.expansion.attribute.*;
import cn.donting.jme.expansion.attribute.lmpl.*;
import cn.donting.jme.inspect.Inspect;
import cn.donting.jme.ioc.Bean;
import cn.donting.jme.swing.tab.InspectTab;
import cn.donting.jme.utile.J3mUtil;
import cn.hutool.core.io.FileUtil;
import com.jme3.asset.AssetKey;
import com.jme3.asset.MaterialKey;
import com.jme3.material.MatParam;
import com.jme3.material.Material;
import com.jme3.material.MaterialDef;
import com.jme3.math.ColorRGBA;
import com.jme3.scene.Geometry;
import com.jme3.shader.VarType;
import com.jme3.util.blockparser.BlockLanguageParser;
import com.jme3.util.blockparser.Statement;
import lombok.extern.slf4j.Slf4j;

import javax.swing.*;
import java.awt.datatransfer.DataFlavor;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Bean
public class MaterialInspectBuilder implements InspectBuilder<Object> {
    private static final String whitespacePattern = "\\p{javaWhitespace}+";

    @Override
    public boolean isHandle(Object o) {
        return o instanceof Material || o instanceof Geometry;
    }

    @Override
    public void builder(Inspect inspect, Object object) throws Exception {
        if (object instanceof Material) {
            builder(inspect, (Material) object);
        }
        if (object instanceof Geometry) {
            builder(inspect, (Geometry) object);
        }
    }

    public void builder(Inspect inspect, Geometry geometry) {
        Material material = geometry.getMaterial();
        TitlePaneAttributeGroup materialGroup = new TitlePaneAttributeGroup("Material");
        materialGroup.setKey("Material");
        List<Attribute> attributes = getAttributes(material);
        materialGroup.addAttribute(attributes);
        inspect.addAttributeGroup(materialGroup);
        inspect.addTransferHandler(new MaterialTransferHandler(geometry));
    }

    private static List<Attribute> getAttributes(Material material){
        List<Attribute> attributes=new ArrayList<>();
        StringAttribute stringAttribute = new StringAttribute("def: ");
        stringAttribute.setValue(material.getMaterialDef().getAssetName());
        stringAttribute.setInputEnable(false);
        attributes.add(stringAttribute);
        if (material.getKey() != null) {
            AssetKey key = material.getKey();
            String name = key.getName();
            StringAttribute assetKeyAttribute = new StringAttribute("AssetKey: ");
            assetKeyAttribute.setValue(name);
            assetKeyAttribute.setInputEnable(false);
            attributes.add(assetKeyAttribute);
        }
        return attributes;
    }

    public void builder(Inspect inspect, Material material) throws IOException {
        String assetName = material.getAssetName();

        HashSet<String> colors = materialParColor(material);

        BaseInfoAttributeGroup baseInfoAttributeGroup = new BaseInfoAttributeGroup();
        baseInfoAttributeGroup.setName(material.getName());
        baseInfoAttributeGroup.setType(material.getMaterialDef().getAssetName());
        baseInfoAttributeGroup.setNameChange(newValue -> {
            material.setName(newValue);
            saveMaterial(material);
        });
        inspect.setTop(baseInfoAttributeGroup);



        TitlePaneAttributeGroup materialParametersGroup = new TitlePaneAttributeGroup("MaterialParameters");
        materialParametersGroup.setExpand(true);
        MaterialDef materialDef = material.getMaterialDef();
        Collection<MatParam> materialParams = materialDef.getMaterialParams();

        List<BooleanAttribute> booleanAttributes = new ArrayList<>();
        List<ColorAttribute> colorAttributes = new ArrayList<>();
        List<FloatAttribute> floatAttributes = new ArrayList<>();
        List<IntegerAttribute> integerAttributes = new ArrayList<>();
        List<Vector3fAttribute> vector3fAttributes = new ArrayList<>();
        List<Vector4fAttribute> vector4fAttributes = new ArrayList<>();

        for (MatParam materialParam : materialParams) {
            VarType varType = materialParam.getVarType();
            String name = materialParam.getName() + ": ";
            Object paramValue = material.getParamValue(materialParam.getName());
            boolean enable = paramValue != null;
            Attribute attribute = null;
            switch (varType) {
                case Float -> {
                    FloatAttribute floatAttribute = new FloatAttribute(name);
                    floatAttributes.add(floatAttribute);
                    attribute = floatAttribute;
                }
                case Int -> {
                    IntegerAttribute integerAttribute = new IntegerAttribute(name);
                    integerAttributes.add(integerAttribute);
                    attribute = integerAttribute;
                }
                case Boolean -> {
                    BooleanAttribute booleanAttribute = new BooleanAttribute(name);
                    booleanAttributes.add(booleanAttribute);
                    attribute = booleanAttribute;
                }
                case Vector4 -> {
                    if(paramValue instanceof ColorRGBA || colors.contains(materialParam.getName())){
                        ColorAttribute colorAttribute = new ColorAttribute(name);
                        colorAttributes.add(colorAttribute);
                        attribute = colorAttribute;
                    }else {
                        Vector4fAttribute vector4fAttribute = new Vector4fAttribute(name);
                        vector4fAttributes.add(vector4fAttribute);
                        attribute = vector4fAttribute;
                    }
                }
                case Vector3 -> {
                    Vector3fAttribute vector3fAttribute = new Vector3fAttribute(name);
                    vector3fAttributes.add(vector3fAttribute);
                    attribute = vector3fAttribute;

                }
                default -> {
//                    paneAttributeGroup.addAttribute(new LabelAttribute(name + " not support:" + varType.name()));
                    break;
                }
            }

            if (paramValue != null && attribute != null) {
                attribute.setValue(paramValue);
            }
            if (attribute != null) {
                attribute.showEnableCheckBox(true);
                attribute.setEnable(enable);
                attribute.setValueChangeListener(newValue -> {
                    if (newValue == null) {
                        material.clearParam(materialParam.getName());
                    } else {
                        material.setParam(materialParam.getName(), varType, newValue);
                    }
                    saveMaterial(material);
                });
            }
        }

        addAttributes(booleanAttributes, materialParametersGroup);
        addAttributes(colorAttributes, materialParametersGroup);
        addAttributes(floatAttributes, materialParametersGroup);
        addAttributes(integerAttributes, materialParametersGroup);
        addAttributes(vector3fAttributes, materialParametersGroup);
        addAttributes(vector4fAttributes, materialParametersGroup);
        inspect.addAttributeGroup(materialParametersGroup);
    }

    private void saveMaterial(Material material) {
        AssetKey key = material.getKey();
        String name = key.getName();
        File file = new File(EditorContent.getProject().getSrcResourcesDir(), name);
        String j3mText = J3mUtil.toJ3mText(material);
        FileUtil.writeBytes(j3mText.getBytes(StandardCharsets.UTF_8), file);
    }

    private void addAttributes(List list, AttributeGroup group) {
        group.addAttribute(list);
    }

    /**
     * 解析J3md 中的  MaterialParameters 中的 color 参数key
     * @param material
     * @return
     * @throws IOException
     */
    private HashSet<String> materialParColor(Material material) throws IOException {
        HashSet<String> colors=new HashSet<>();
        URL resource = EditorContent.getResource(material.getMaterialDef().getAssetName());
        List<Statement> parse = BlockLanguageParser.parse(resource.openStream());
        Statement statement = parse.get(0);
        for (Statement content : statement.getContents()) {
            if (content.getLine().equals("MaterialParameters")) {
                List<Statement> contents = content.getContents();
                for (Statement materialParameter : contents) {
                    String line = materialParameter.getLine(); // Parse ffbinding
                    //处理 默认值 ：
                    String[] split = line.split(":");
                    // Parse default val
                    if (split.length == 1){
                        // Doesn't contain default value
                    }else{
                        if (split.length != 2){
                            throw new IOException("Parameter statement syntax incorrect");
                        }
                        line = split[0].trim();
//                        defaultVal = split[1].trim();
                    }
                    //处理 ()
                    int startParen = line.indexOf("(");
                    if (startParen != -1){
                        // get content inside parentheses
                        int endParen = line.indexOf(")", startParen);
                        String bindingStr = line.substring(startParen+1, endParen).trim();
                        // don't care about bindingStr
                        line = line.substring(0, startParen);
                    }
                    //分割类型和key
                    split = line.split(whitespacePattern);
                    if (split[0].equals("Color")) {
                        colors.add(split[1]);
                    }
                }
                return colors;
            }
        }
        return colors;
    }
    @Slf4j
    private static class MaterialTransferHandler extends TransferHandler {
        private Geometry geometry;

        public MaterialTransferHandler(Geometry geometry) {
            this.geometry = geometry;
        }

        @Override
        public boolean canImport(TransferSupport support) {
            try {
                if (support.isDataFlavorSupported(new DataFlavor("jme/file-j3m"))) {
                    return true;
                }
            } catch (ClassNotFoundException e) {
                log.warn(e.getMessage(),e);
                return false;
            }
            return false;
        }

        @Override
        public boolean importData(TransferSupport support) {
            if (!canImport(support)) {
                return false;
            }
            try {
                File file = (File)support.getTransferable().getTransferData(new DataFlavor("jme/file-j3m"));
                File srcResourcesDir = EditorContent.getProject().getSrcResourcesDir();
                String name = FileUtil.subPath(srcResourcesDir.getCanonicalPath(), file.getCanonicalPath());
                Material material = EditorContent.getAssetManagerFromCache(new MaterialKey(name));
                geometry.setMaterial(material);
                InspectTab tab = EditorContent.getEditorUI().getTab(1, InspectTab.class);
                if(tab!=null){
                    Inspect inspect = tab.getInspect();
                    List<AttributeGroup> attributeGroups = inspect.getAttributeGroups();
                    for (AttributeGroup attributeGroup : attributeGroups) {
                        if ("Material".equals(attributeGroup.getKey())) {
                            attributeGroup.clear();
                            List<Attribute> attributes = MaterialInspectBuilder.getAttributes(material);
                            attributeGroup.addAttribute(attributes);
                            tab.updateGroup();
                            return true;
                        }
                    }
                }
            } catch (Exception e) {
                log.warn(e.getMessage(),e);
                return false;
            }

            return true;

        }
    }
}
